package com.imut.lagain.controller;

import com.imut.lagain.entity.MemoryEntry;
import com.imut.lagain.service.IMemoryEntryService;
import com.imut.lagain.util.FileUtil;
import com.imut.lagain.util.JwtUtil;
import jakarta.validation.Valid;
import jakarta.validation.constraints.*;
import lombok.RequiredArgsConstructor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.Resource;
import org.springframework.core.io.UrlResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.net.MalformedURLException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import jakarta.servlet.http.HttpServletRequest;

/**
 * 文件上传控制器
 */
@RestController
@RequestMapping("/files")
@RequiredArgsConstructor
@Validated
public class FileController {
    private static final Logger log = LoggerFactory.getLogger(FileController.class);
    
    @Value("${file.upload.path}")
    private String uploadPath;
    
    @Value("${file.access.url}")
    private String accessUrl;
    
    private final IMemoryEntryService memoryEntryService;
    private final JwtUtil jwtUtil;
    private final FileUtil fileUtil;
    
    /**
     * 上传图片
     * @param file 图片文件
     * @param authorization JWT令牌
     * @return 上传结果
     */
    @PostMapping("/upload/image")
    public ResponseEntity<Map<String, Object>> uploadImage(
            @RequestParam("file") @NotNull MultipartFile file,
            @RequestHeader("Authorization") @NotBlank String authorization) {
        Map<String, Object> response = new HashMap<>();
        
        try {
            log.info("开始处理图片上传请求");
            log.info("文件名: {}, 文件大小: {}", file.getOriginalFilename(), file.getSize());
            log.info("Authorization header: {}", authorization);
            
            // 检查Authorization头格式
            if (!authorization.startsWith("Bearer ")) {
                log.warn("Authorization头格式不正确: {}", authorization);
                response.put("success", false);
                response.put("message", "Authorization头格式不正确");
                return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(response);
            }
            
            String token = authorization.substring(7); // 移除 "Bearer " 前缀
            log.info("提取的token: {}", token);
            
            // 验证token
            if (!jwtUtil.validateToken(token)) {
                log.warn("Token验证失败");
                response.put("success", false);
                response.put("message", "Token无效或已过期");
                return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(response);
            }
            
            log.info("Token验证成功");
            Long userId = jwtUtil.getUserIdFromToken(token);
            log.info("从token中提取的用户ID: {}", userId);
            
            if (userId == null) {
                log.warn("无法从token中提取用户ID");
                response.put("success", false);
                response.put("message", "无法获取用户信息");
                return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(response);
            }
            
            // 添加用户身份验证日志
            log.info("开始处理文件上传，用户ID: {}", userId);
            
            String fileUrl = fileUtil.uploadImage(file);
            log.info("文件上传成功，访问URL: {}", fileUrl);
            
            response.put("success", true);
            response.put("fileUrl", fileUrl);
            response.put("fileSize", file.getSize());
            response.put("message", "图片上传成功");
            return ResponseEntity.ok(response);
            
        } catch (Exception e) {
            log.error("Failed to upload image", e);
            response.put("success", false);
            response.put("message", "图片上传失败: " + e.getMessage());
            return ResponseEntity.internalServerError().body(response);
        }
    }
    
    /**
     * 上传语音
     * @param file 语音文件
     * @param authorization JWT令牌
     * @return 上传结果
     */
    @PostMapping("/upload/audio")
    public ResponseEntity<Map<String, Object>> uploadAudio(
            @RequestParam("file") @NotNull MultipartFile file,
            @RequestHeader("Authorization") @NotBlank String authorization) {
        Map<String, Object> response = new HashMap<>();
        
        try {
            // 检查Authorization头格式
            if (!authorization.startsWith("Bearer ")) {
                log.warn("Authorization头格式不正确: {}", authorization);
                response.put("success", false);
                response.put("message", "Authorization头格式不正确");
                return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(response);
            }
            
            String token = authorization.substring(7); // 移除 "Bearer " 前缀
            
            // 验证token
            if (!jwtUtil.validateToken(token)) {
                log.warn("Token验证失败");
                response.put("success", false);
                response.put("message", "Token无效或已过期");
                return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(response);
            }
            
            log.info("开始处理语音上传");
            
            String fileUrl = fileUtil.uploadAudio(file);
            log.info("语音上传成功，访问URL: {}", fileUrl);
            
            response.put("success", true);
            response.put("fileUrl", fileUrl);
            response.put("fileSize", file.getSize());
            response.put("message", "语音上传成功");
            return ResponseEntity.ok(response);
            
        } catch (Exception e) {
            log.error("Failed to upload audio", e);
            response.put("success", false);
            response.put("message", "语音上传失败: " + e.getMessage());
            return ResponseEntity.internalServerError().body(response);
        }
    }
    
    /**
     * 上传文件（通用）
     * @param file 文件
     * @param type 文件类型 (image/audio/voice/document)
     * @param authorization JWT令牌
     * @return 上传结果
     */
    @PostMapping("/upload")
    public ResponseEntity<Map<String, Object>> uploadFile(
            @RequestParam("file") @NotNull MultipartFile file,
            @RequestParam(value = "type", defaultValue = "image") String type,
            @RequestHeader("Authorization") @NotBlank String authorization) {
        Map<String, Object> response = new HashMap<>();
        
        try {
            log.info("开始处理文件上传请求，类型: {}", type);
            
            // 检查Authorization头格式
            if (!authorization.startsWith("Bearer ")) {
                response.put("success", false);
                response.put("message", "Authorization头格式不正确");
                return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(response);
            }
            
            String token = authorization.substring(7); // 移除 "Bearer " 前缀
            
            // 验证token
            if (!jwtUtil.validateToken(token)) {
                response.put("success", false);
                response.put("message", "Token无效或已过期");
                return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(response);
            }
            
            Long userId = jwtUtil.getUserIdFromToken(token);
            if (userId == null) {
                response.put("success", false);
                response.put("message", "无法获取用户信息");
                return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(response);
            }
            
            String fileUrl;
            switch (type.toLowerCase()) {
                case "image":
                    fileUrl = fileUtil.uploadImage(file);
                    break;
                case "audio":
                case "voice":
                    fileUrl = fileUtil.uploadAudio(file);
                    break;
                default:
                    response.put("success", false);
                    response.put("message", "不支持的文件类型: " + type);
                    return ResponseEntity.badRequest().body(response);
            }
            
            response.put("success", true);
            response.put("fileUrl", fileUrl);
            response.put("fileSize", file.getSize());
            response.put("message", "文件上传成功");
            return ResponseEntity.ok(response);
            
        } catch (Exception e) {
            log.error("Failed to upload file", e);
            response.put("success", false);
            response.put("message", "文件上传失败: " + e.getMessage());
            return ResponseEntity.internalServerError().body(response);
        }
    }
    
    /**
     * 添加图片条目到胶囊
     * @param capsuleId 胶囊ID
     * @param authorization JWT令牌
     * @param request 添加请求
     * @return 添加结果
     */
    @PostMapping("/capsules/{capsuleId}/entries/image")
    public ResponseEntity<Map<String, Object>> addImageEntry(
            @PathVariable @Min(1) Long capsuleId,
            @RequestHeader("Authorization") @NotBlank String authorization,
            @Valid @RequestBody AddImageEntryRequest request) {
        Map<String, Object> response = new HashMap<>();
        
        try {
            // 检查Authorization头格式
            if (!authorization.startsWith("Bearer ")) {
                log.warn("Authorization头格式不正确: {}", authorization);
                response.put("success", false);
                response.put("message", "Authorization头格式不正确");
                return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(response);
            }
            
            String token = authorization.substring(7); // 移除 "Bearer " 前缀
            
            // 验证token
            if (!jwtUtil.validateToken(token)) {
                log.warn("Token验证失败");
                response.put("success", false);
                response.put("message", "Token无效或已过期");
                return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(response);
            }
            
            Long userId = jwtUtil.getUserIdFromToken(token);
            log.info("验证通过的用户ID: {}", userId);
            
            if (userId == null) {
                log.warn("无法从token中提取用户ID");
                response.put("success", false);
                response.put("message", "无法获取用户信息");
                return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(response);
            }
            
            MemoryEntry entry = memoryEntryService.createImageEntry(
                    capsuleId, request.getFileUrl(), request.getFileName(), request.getFileSize());
            
            if (entry != null) {
                response.put("success", true);
                response.put("data", entry);
                response.put("message", "图片条目添加成功");
                return ResponseEntity.ok(response);
            } else {
                response.put("success", false);
                response.put("message", "图片条目添加失败");
                return ResponseEntity.internalServerError().body(response);
            }
            
        } catch (Exception e) {
            log.error("Failed to add image entry", e);
            response.put("success", false);
            response.put("message", "添加图片条目失败");
            return ResponseEntity.internalServerError().body(response);
        }
    }
    
    /**
     * 添加语音条目到胶囊
     * @param capsuleId 胶囊ID
     * @param authorization JWT令牌
     * @param request 添加请求
     * @return 添加结果
     */
    @PostMapping("/capsules/{capsuleId}/entries/audio")
    public ResponseEntity<Map<String, Object>> addAudioEntry(
            @PathVariable @Min(1) Long capsuleId,
            @RequestHeader("Authorization") @NotBlank String authorization,
            @Valid @RequestBody AddAudioEntryRequest request) {
        Map<String, Object> response = new HashMap<>();
        
        try {
            // 检查Authorization头格式
            if (!authorization.startsWith("Bearer ")) {
                log.warn("Authorization头格式不正确: {}", authorization);
                response.put("success", false);
                response.put("message", "Authorization头格式不正确");
                return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(response);
            }
            
            String token = authorization.substring(7); // 移除 "Bearer " 前缀
            
            // 验证token
            if (!jwtUtil.validateToken(token)) {
                log.warn("Token验证失败");
                response.put("success", false);
                response.put("message", "Token无效或已过期");
                return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(response);
            }
            
            Long userId = jwtUtil.getUserIdFromToken(token);
            log.info("验证通过的用户ID: {}", userId);
            
            if (userId == null) {
                log.warn("无法从token中提取用户ID");
                response.put("success", false);
                response.put("message", "无法获取用户信息");
                return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(response);
            }
            
            MemoryEntry entry = memoryEntryService.createAudioEntry(
                    capsuleId, request.getFileUrl(), request.getFileName(), request.getFileSize(), request.getDuration());
            
            if (entry != null) {
                response.put("success", true);
                response.put("data", entry);
                response.put("message", "语音条目添加成功");
                return ResponseEntity.ok(response);
            } else {
                response.put("success", false);
                response.put("message", "语音条目添加失败");
                return ResponseEntity.internalServerError().body(response);
            }
            
        } catch (Exception e) {
            log.error("Failed to add audio entry", e);
            response.put("success", false);
            response.put("message", "添加语音条目失败");
            return ResponseEntity.internalServerError().body(response);
        }
    }
    
    /**
     * 访问上传的文件
     * @param request HTTP请求
     * @return 文件资源
     */
    @GetMapping("/**")
    public ResponseEntity<Resource> serveFile(HttpServletRequest request) {
        try {
            // 从请求路径中提取文件路径
            String fullPath = request.getRequestURI();
            String contextPath = request.getContextPath();
            
            // 移除上下文路径前缀
            if (fullPath.startsWith(contextPath)) {
                fullPath = fullPath.substring(contextPath.length());
            }
            
            // 移除文件服务的基础路径前缀
            String filePath = fullPath;
            if (filePath.startsWith("/api/files/")) {
                filePath = filePath.substring("/api/files".length());
            } else if (filePath.startsWith("/files/")) {
                filePath = filePath.substring("/files".length());
            }
            
            // 确保路径以分隔符开头
            if (!filePath.startsWith("/")) {
                filePath = "/" + filePath;
            }
            
            // 规范化路径，防止路径遍历攻击
            // 移除开头的斜杠，因为我们要构建相对路径
            if (filePath.startsWith("/")) {
                filePath = filePath.substring(1);
            }
            
            // 构建目标文件路径
            Path basePath = Paths.get(uploadPath).toAbsolutePath().normalize();
            Path targetPath = basePath.resolve(filePath).normalize();
            
            // 验证目标路径是否在上传目录下（防止路径遍历攻击）
            if (!targetPath.startsWith(basePath)) {
                log.warn("非法路径尝试: {}", filePath);
                return ResponseEntity.notFound().build();
            }
            
            log.info("尝试访问文件: {}", targetPath.toString());
            
            // 检查文件是否存在
            if (!Files.exists(targetPath)) {
                log.warn("文件不存在: {}", targetPath.toString());
                return ResponseEntity.notFound().build();
            }
            
            // 检查是否为目录
            if (Files.isDirectory(targetPath)) {
                log.warn("请求的路径是目录: {}", targetPath.toString());
                return ResponseEntity.notFound().build();
            }
            
            Resource resource = new UrlResource(targetPath.toUri());
            if (resource.exists() && resource.isReadable()) {
                String contentType = getFileContentType(targetPath.toString());
                log.info("成功加载文件: {}, Content-Type: {}", targetPath.toString(), contentType);
                return ResponseEntity.ok()
                        .contentType(MediaType.parseMediaType(contentType))
                        .body(resource);
            } else {
                log.warn("文件不可读或不存在: {}", targetPath.toString());
                return ResponseEntity.notFound().build();
            }
        } catch (Exception e) {
            log.error("文件服务异常: ", e);
            return ResponseEntity.internalServerError().build();
        }
    }
    
    /**
     * 获取文件系统路径
     * @param filePath 请求的文件路径
     * @return 文件系统路径
     */
    private String getFileSystemPath(String filePath) {
        // 移除开头的斜杠
        if (filePath.startsWith("/")) {
            filePath = filePath.substring(1);
        }
        return Paths.get(uploadPath, filePath).toString();
    }
    
    /**
     * 获取允许访问的目录列表
     * @return 允许访问的目录列表
     */
    private Iterable<String> getAllowedDirectories() {
        // 返回包含上传路径的列表，可以根据需要添加更多允许的目录
        List<String> allowedDirs = new ArrayList<>();
        allowedDirs.add(uploadPath);
        // 添加images子目录
        allowedDirs.add(Paths.get(uploadPath, "images").toString());
        // 添加audios子目录
        allowedDirs.add(Paths.get(uploadPath, "audios").toString());
        // 添加voices子目录
        allowedDirs.add(Paths.get(uploadPath, "voices").toString());
        // 添加documents子目录
        allowedDirs.add(Paths.get(uploadPath, "documents").toString());
        return allowedDirs;
    }
    
    /**
     * 获取文件内容类型
     * @param filePath 文件路径
     * @return 内容类型
     */
    private String getFileContentType(String filePath) throws IOException {
        Path path = Paths.get(filePath);
        String contentType = Files.probeContentType(path);
        return contentType != null ? contentType : "application/octet-stream";
    }
    
    /**
     * 获取文件服务的基础路径映射
     * @return 基础路径映射
     */
    private Map<String, String> getBasePaths() {
        Map<String, String> basePaths = new HashMap<>();
        basePaths.put("file", "/files");
        basePaths.put("upload", "/upload"); // 添加上传路径映射
        // 可以添加更多基础路径映射
        return basePaths;
    }
    
    /**
     * 添加图片条目请求DTO
     */
    public static class AddImageEntryRequest {
        @NotBlank(message = "文件URL不能为空")
        @Size(max = 500, message = "文件URL长度不能超过500个字符")
        private String fileUrl;
        
        @NotNull(message = "文件大小不能为空")
        @Min(value = 1, message = "文件大小必须大于0")
        private Long fileSize;
        
        @Size(max = 255, message = "文件名长度不能超过255个字符")
        private String fileName;
        
        public String getFileUrl() {
            return fileUrl;
        }
        
        public void setFileUrl(String fileUrl) {
            this.fileUrl = fileUrl;
        }
        
        public Long getFileSize() {
            return fileSize;
        }
        
        public void setFileSize(Long fileSize) {
            this.fileSize = fileSize;
        }
        
        public String getFileName() {
            return fileName;
        }
        
        public void setFileName(String fileName) {
            this.fileName = fileName;
        }
    }
    
    /**
     * 添加语音条目请求DTO
     */
    public static class AddAudioEntryRequest {
        @NotBlank(message = "文件URL不能为空")
        @Size(max = 500, message = "文件URL长度不能超过500个字符")
        private String fileUrl;
        
        @NotNull(message = "文件大小不能为空")
        @Min(value = 1, message = "文件大小必须大于0")
        private Long fileSize;
        
        @NotNull(message = "音频时长不能为空")
        @Min(value = 1, message = "音频时长必须大于0秒")
        private Integer duration;
        
        @Size(max = 255, message = "文件名长度不能超过255个字符")
        private String fileName;
        
        public String getFileUrl() {
            return fileUrl;
        }
        
        public void setFileUrl(String fileUrl) {
            this.fileUrl = fileUrl;
        }
        
        public Long getFileSize() {
            return fileSize;
        }
        
        public void setFileSize(Long fileSize) {
            this.fileSize = fileSize;
        }
        
        public Integer getDuration() {
            return duration;
        }
        
        public void setDuration(Integer duration) {
            this.duration = duration;
        }
        
        public String getFileName() {
            return fileName;
        }
        
        public void setFileName(String fileName) {
            this.fileName = fileName;
        }
    }
}